--
--	Package File Template
--
--	Purpose: This package defines supplemental types, subtypes, 
--		 constants, and functions 
--
--   To use any of the example code shown below, uncomment the lines and modify as necessary
--

library IEEE;
use IEEE.STD_LOGIC_1164.all;

library IEEE;
use IEEE.NUMERIC_STD.all;

package game_support is

	------------------------------------------
	-- Definitions for Screen Dimensions -----
	------------------------------------------
	
	constant SCREEN_WIDTH : positive := 800;
	constant SCREEN_HEIGHT : positive := 600;
	
	--------------------------------------------
	-- Type Definitions ------------------------
	--------------------------------------------
	
	subtype x_position is natural range 0 to SCREEN_WIDTH;
	subtype y_position is natural range 0 to SCREEN_HEIGHT;
	
	type position is
		record
			x	: x_position;
			y	: y_position;
		end record;
		
	type rect is
		record
			width : x_position;
			height : y_position;
		end record;
		
	type paddle_direction is (stay, left, right);
	type ball_direction is (stay, northeast, southeast, southwest, northwest);
	type collision_type is (none, left_collision, right_collision, top_collision, bottom_collision,
		paddle1_collision, paddle2_collision);
	
	------------------------------------------
	-- Paddle Dimension Definitions ----------
	------------------------------------------
	
	constant PADDLE_BOUNDS : rect := (width => 100, height => 10);

	constant PADDLE_X_INIT : x_position := SCREEN_WIDTH/2 - PADDLE_BOUNDS.width/2;
	
	constant PADDLE1_Y : y_position := SCREEN_HEIGHT - 20;
	constant PADDLE2_Y : y_position := 10;
	
	
	-------------------------------------------
	-- Ball Dimension Definitions -------------
	-------------------------------------------
	
	constant BALL_SIZE : positive := 10;
	
	constant BALL_INIT : position := (x => SCREEN_WIDTH/2 - BALL_SIZE/2, y => PADDLE1_Y - 10);


	-------------------------------------------
	-- Brick Dimension Definitions -------------
	-------------------------------------------
	constant BRICK_BOUNDS : rect := (width => 50, height => 30);

-- type <new_type> is
--  record
--    <type_name>        : std_logic_vector( 7 downto 0);
--    <type_name>        : std_logic;
-- end record;
--
-- Declare constants
--
-- constant <constant_name>		: time := <time_unit> ns;
-- constant <constant_name>		: integer := <value;
--
-- Declare functions and procedure
--
-- function <function_name>  (signal <signal_name> : in <type_declaration>) return <type_declaration>;
-- procedure <procedure_name> (<type_declaration> <constant_name>	: in <type_declaration>);
--

	function in_paddle_bounds (signal corner : in position; signal cursor_x : in std_logic_vector(10 downto 0); signal cursor_y : in std_logic_vector(10 downto 0)) return boolean;
	function in_ball_bounds (signal corner : in position; signal cursor_x : in std_logic_vector(10 downto 0); signal cursor_y : in std_logic_vector(10 downto 0)) return boolean;
	function get_new_direction (signal old_direction : in ball_direction; signal collision : collision_type) return ball_direction;
	function get_new_position (signal old_position : in position; signal direction : in ball_direction; signal collision : in collision_type) return position;
end game_support;

package body game_support is


	function in_paddle_bounds (signal corner : in position; signal cursor_x : in std_logic_vector(10 downto 0); signal cursor_y : in std_logic_vector(10 downto 0)) return boolean is
	begin
		return unsigned(cursor_x) >= corner.x and unsigned(cursor_x) < (corner.x + PADDLE_BOUNDS.width)
			and unsigned(cursor_y) >= corner.y and unsigned(cursor_y) < (corner.y + PADDLE_BOUNDS.height);
	end in_paddle_bounds;
	
	function in_ball_bounds (signal corner : in position; signal cursor_x : in std_logic_vector(10 downto 0); signal cursor_y : in std_logic_vector(10 downto 0)) return boolean is
	begin
		return unsigned(cursor_x) >= corner.x and unsigned(cursor_x) < (corner.x + BALL_SIZE)
			and unsigned(cursor_y) >= corner.y and unsigned(cursor_y) < (corner.y + BALL_SIZE);
	end in_ball_bounds;
	
	function get_new_direction (signal old_direction : in ball_direction; signal collision : collision_type) return ball_direction is
		variable next_ball_direction : ball_direction;
	begin
		case collision is
				when left_collision => 
					if old_direction = northwest then
						next_ball_direction := northeast;
					elsif old_direction = southwest then
						next_ball_direction := southeast;
					else
						next_ball_direction := old_direction;
					end if;
				when right_collision => 
					if old_direction = northeast then
						next_ball_direction := northwest;
					elsif old_direction = southeast then
						next_ball_direction := southwest;
					else
						next_ball_direction := old_direction;
					end if;		
				when paddle1_collision =>
					if old_direction = southwest then
						next_ball_direction := northwest;
					elsif old_direction = southeast then
						next_ball_direction := northeast;
					else
						next_ball_direction := old_direction;
					end if;
				when paddle2_collision =>
					if old_direction = northwest then
						next_ball_direction := southwest;
					elsif old_direction = northeast then
						next_ball_direction := southeast;
					else
						next_ball_direction := old_direction;
					end if;
--						next_ball_direction := stay;
				when top_collision =>
					if old_direction = northwest then
						next_ball_direction := southwest;
					elsif old_direction = northeast then
						next_ball_direction := southeast;
					else
						next_ball_direction := old_direction;
					end if;
					
				when bottom_collision =>
					if old_direction = southwest then
						next_ball_direction := northwest;
					elsif old_direction = southeast then
						next_ball_direction := northeast;
					else
						next_ball_direction := old_direction;
					end if;
				when none =>
					next_ball_direction := old_direction;
				when others =>
			end case;
		return next_ball_direction;
	end get_new_direction;
	
	
	function get_new_position (signal old_position : in position; signal direction : in ball_direction; signal collision : in collision_type) return position is
			variable new_position : position;
	begin
		case collision is
			when none | paddle1_collision | paddle2_collision =>
				case direction is
					when northeast =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y - 1;
					when southeast =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y + 1;
					when southwest =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y + 1;
					when northwest =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y - 1;
					when stay =>
						new_position.x := old_position.x;
						new_position.y := old_position.y;
				end case;
				
			when top_collision =>
				case direction is
					when northeast =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y + 1;
					when northwest =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y + 1;
					when others =>
						new_position := old_position;
				end case;
			when bottom_collision =>
				case direction is
					when southeast =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y - 1;
					when southwest =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y - 1;
					when others =>
						new_position := old_position;
				end case;
			when left_collision =>
				case direction is
					when northwest =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y - 1;
					when southwest =>
						new_position.x := old_position.x + 1;
						new_position.y := old_position.y + 1;
					when others =>
						new_position := old_position;
				end case;
			when right_collision =>
				case direction is
					when northeast =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y - 1;
					when southeast =>
						new_position.x := old_position.x - 1;
						new_position.y := old_position.y + 1;
					when others =>
						new_position := old_position;
				end case;
		end case;
		
		return new_position;
			
	end get_new_position;

---- Example 1
--  function <function_name>  (signal <signal_name> : in <type_declaration>  ) return <type_declaration> is
--    variable <variable_name>     : <type_declaration>;
--  begin
--    <variable_name> := <signal_name> xor <signal_name>;
--    return <variable_name>; 
--  end <function_name>;

---- Example 2
--  function <function_name>  (signal <signal_name> : in <type_declaration>;
--                         signal <signal_name>   : in <type_declaration>  ) return <type_declaration> is
--  begin
--    if (<signal_name> = '1') then
--      return <signal_name>;
--    else
--      return 'Z';
--    end if;
--  end <function_name>;

---- Procedure Example
--  procedure <procedure_name>  (<type_declaration> <constant_name>  : in <type_declaration>) is
--    
--  begin
--    
--  end <procedure_name>;
 
end game_support;
